﻿#include "GUI.h"
#include "smallC.h"
#include <sstream>

vector<instruction>PPcode;
vector<string>EMS;
vector<string> Table;
int dastack[datamaxn];
vector<int> inputdata;
int inputindex;
int pcindex;
int p;
int b;
int f;

GUI::GUI(QWidget *parent)
    : QMainWindow(parent)
{
    // 主窗口
    ui.setupUi(this);
    ui.mainToolBar->setVisible(false);
    this->setFont(QFont("Microsoft YaHei", 13));
    this->resize(1100, 800);
    this->setWindowTitle("smallC");
    
    // 菜单栏
    bar = new QMenuBar();
    bar->setFont(QFont("Microsoft YaHei", 10));
    setMenuBar(bar);
    fileMenu = bar->addMenu("文件(&F)");
    openAction = fileMenu->addAction("打开(&O)");
    openAction->setShortcut(QKeySequence::Open);
    connect(openAction, SIGNAL(triggered()), this, SLOT(OpenFile()));
    saveAction = fileMenu->addAction("保存(&S)");
    saveAction->setShortcut(QKeySequence::Save);
    connect(saveAction, SIGNAL(triggered()), this, SLOT(SaveFile()));
    fileMenu->addSeparator();
    exitAction = fileMenu->addAction("退出(&E)");
    connect(exitAction, SIGNAL(triggered()), this, SLOT(Exit()));
    aboutMenu = bar->addMenu("关于(&A)");
    authorAction = aboutMenu->addAction("作者(&A)");
    connect(authorAction, SIGNAL(triggered()), this, SLOT(Author()));
    intruduceAction = aboutMenu->addAction("介绍(&I)");
    connect(intruduceAction, SIGNAL(triggered()), this, SLOT(Introduce()));

    // 代码
    codeEdit = new QTextEdit(this);
    codeEdit->setGeometry(25, 50, 500, 725);

    // 虚拟机指令
    pcodeEdit = new QTextEdit(this);
    pcodeEdit->setGeometry(550, 50, 250, 500);
    pcodeEdit->setReadOnly(true);

    // 数据栈
    dstackEdit = new QTextEdit(this);
    dstackEdit->setGeometry(825, 50, 250, 500);
    dstackEdit->setReadOnly(true);

    // 编译信息
    commsEdit = new QTextEdit(this);
    commsEdit->setGeometry(550, 575, 250, 125);
    commsEdit->setReadOnly(true);

    // 输入输出
    inoutEdit = new QTextEdit(this);
    inoutEdit->setGeometry(825, 575, 250, 125);
    inoutEdit->setPlaceholderText("请在运行前输入所需数据");

    comButton = new QPushButton(this);
    comButton->setGeometry(600, 725, 150, 50);
    comButton->setText("编译");
    connect(comButton, SIGNAL(clicked()), this, SLOT(Compiler()));

    ssButton = new QPushButton(this);
    ssButton->setGeometry(825, 725, 100, 50);
    ssButton->setText("单步执行");
    ssButton->setEnabled(false);
    connect(ssButton, SIGNAL(clicked()), this, SLOT(SingleInterpret()));

    exButton = new QPushButton(this);
    exButton->setGeometry(975, 725, 100, 50);
    exButton->setText("全部执行");
    exButton->setEnabled(false);
    connect(exButton, SIGNAL(clicked()), this, SLOT(Interpret()));
}

GUI::~GUI()
{}

void GUI::OpenFile()
{
    QString curPath, fileName;
    curPath = QCoreApplication::applicationDirPath();
    fileName = QFileDialog::getOpenFileName(this, tr("打开文件"), curPath,
        "所有文件(*.*);;文本文件(*.txt);;C++程序文件(*.cpp)");
    if (!fileName.isEmpty())
    {
        codeEdit->clear();
        QFile file(fileName);
        if (file.open(QIODevice::ReadWrite | QIODevice::Text))
        {
            QTextStream stream(&file);
            while (!stream.atEnd())
                codeEdit->append(stream.readLine());
        }
    }
}

void GUI::SaveFile()
{
    QString curPath, fileName;
    curPath = QCoreApplication::applicationDirPath();
    fileName = QFileDialog::getSaveFileName(this, tr("另存为"), curPath,
        "所有文件(*.*);;文本文件(*.txt);;C++程序文件(*.cpp)");
    if (!fileName.isEmpty())
    {
        QFile file(fileName);
        if (file.open(QIODevice::WriteOnly | QIODevice::Text))
        {
            QTextStream stream(&file);
            stream << codeEdit->toPlainText();
            stream.flush();
            file.close();
        }
    }
}

void GUI::Exit()
{
    qApp->quit();
}

void GUI::Compiler() {
    QFile file("code.txt");
    if (file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QTextStream stream(&file);
        stream << codeEdit->toPlainText();
        stream.flush();
        file.close();
    }
// 编译后执行前需要初始化的变量
    pcodeEdit->clear();
    commsEdit->clear();
    inoutEdit->clear();
    
    dstackEdit->clear();
    inputindex = 0;
    pcindex = 0;
    p = 2;
    b = 1;
    f = 0;
    for (int i = 0; i < datamaxn; i++) dastack[i] = 0;
    if (compiler() == 1) {
        PPcode = GetPCode();
        Table = GetTable();
        PrintPCode();
        PrintTable();
        ssButton->setEnabled(true);
        exButton->setEnabled(true);
    }
    else {
        EMS = GetEMS();
        PrintEMS();
    }
}

void GUI::PrintTable() {
    for (int i = 0; i < Table.size(); i++) {
        commsEdit->append(QString::fromStdString(Table[i]));
    }
}

void GUI::PrintPCode() {
    stringstream ss{};
    for (int i = 0; i < PPcode.size(); i++) {
        ss << i << " ";
        switch (PPcode[i].kind)
        {
        case INT:
            ss << "INT" << " " << PPcode[i].a << endl;
            break;
        case JMP:
            ss << "JMP" << " " << PPcode[i].a << endl;
            break;
        case JPC:
            ss << "JPC" << " " << PPcode[i].a << endl;
            break;
        case LIT:
            ss << "LIT" << " " << PPcode[i].a << endl;
            break;
        case LOD:
            ss << "LOD" << " " << PPcode[i].a << endl;
            break;
        case STO:
            ss << "STO" << " " << PPcode[i].a << endl;
            break;
        case CAL:
            ss << "CAL" << " " << PPcode[i].a << endl;
            break;
        case STP:
            ss << "STP" << " " << PPcode[i].a << endl;
            break;
        case OPR:
            ss << "OPR" << " " << PPcode[i].a << endl;
            break;
        default:
            break;
        }
        string s;
        getline(ss, s);
        pcodeEdit->append(QString::fromStdString(s));
    }
    commsEdit->setText("compile success");
}

void GUI::SingleInterpret() {
    if (b != 0) {
        Execute();
        PDataStack();
    }
    else {
        ssButton->setEnabled(false);
        exButton->setEnabled(false);
    }
}

void GUI::Interpret() {
    while (b != 0) {
        Execute();
    }
    PDataStack();
    exButton->setEnabled(false);
    ssButton->setEnabled(false);
}

void GUI::Execute() {
    if (f == 0) {
        inputdata.clear();
        f = 1;
        stringstream ss;
        ss << inoutEdit->toPlainText().toStdString();
        string s;
        while (ss >> s) inputdata.push_back(atoi(s.c_str()));
    }
    fct t = PPcode[pcindex].kind;
    int a = PPcode[pcindex].a;
    pcindex++;
    if (t == INT) {
        p += a;
    }
    else if (t == JMP) {
        pcindex = a;
    }
    else if (t == JPC) {
        if (dastack[p--] == 0) pcindex = a;
    }
    else if (t == LIT) {
        dastack[++p] = a;
    }
    else if (t == LOD) {
        dastack[++p] = dastack[b + a + 1];
    }
    else if (t == STO) {
        dastack[b + a + 1] = dastack[p--];
    }
    else if (t == CAL) {
        dastack[++p] = b;
        b = p;
        dastack[++p] = pcindex;
        pcindex = a;
    }
    else if (t == STP) {
        for (int i = 0; i < a; i++) {
            dastack[p + 2 - i] = dastack[p - i];
        }
        p = p - a;
    }
    else if (t == OPR) {
        if (a == 0) {
            pcindex = dastack[b + 1];
            int tmp = dastack[b];
            dastack[b] = dastack[p];
            p = b;
            b = tmp;
        }
        else if (a == 1) {
            dastack[p - 1] = dastack[p - 1] % dastack[p];
            p--;
        }
        else if (a == 2) {
            dastack[p - 1] = dastack[p - 1] + dastack[p];
            p--;
        }
        else if (a == 3) {
            dastack[p - 1] = dastack[p - 1] - dastack[p];
            p--;
        }
        else if (a == 4) {
            dastack[p - 1] = dastack[p - 1] * dastack[p];
            p--;
        }
        else if (a == 5) {
            if (dastack[p] == 0) {
                inoutEdit->append("除0错误");
                dastack[p - 1] = 0;
            }
            else {
                dastack[p - 1] = dastack[p - 1] / dastack[p];
            }
            p--;
        }
        else if (a == 6) {
            dastack[p - 1] = (dastack[p - 1] == dastack[p]);
            p--;
        }
        else if (a == 7) {
            dastack[p - 1] = (dastack[p - 1] != dastack[p]);
            p--;
        }
        else if (a == 8) {
            dastack[p - 1] = (dastack[p - 1] < dastack[p]);
            p--;
        }
        else if (a == 9) {
            dastack[p - 1] = (dastack[p - 1] <= dastack[p]);
            p--;
        }
        else if (a == 10) {
            dastack[p - 1] = (dastack[p - 1] > dastack[p]);
            p--;
        }
        else if (a == 11) {
            dastack[p - 1] = (dastack[p - 1] >= dastack[p]);
            p--;
        }
        else if (a == 12) {
            int tmp = GetInputData();
            if (tmp == INT_FAST32_MIN) {
                inoutEdit->append("无有效输入数据");
                b = 0;
            }
            else dastack[++p] = tmp;
        }
        else if (a == 13) {
            inoutEdit->append(QString::fromStdString(to_string(dastack[p--])));
        }
        else if (a == 14) p--;
        else if (a == 15) {
            p++;
            dastack[p] = dastack[p - 1];
        }
    }
    
}

int GUI::GetInputData() {
    if (inputindex < inputdata.size()) return inputdata[inputindex++];
    return INT_FAST32_MIN;
}

void GUI::PDataStack() {
    dstackEdit->clear();
    for (int i = 1; i <= p; i++) dstackEdit->append(QString::fromStdString(to_string(dastack[i])));
}

void GUI::PrintEMS() {
    for (int i = 0; i < EMS.size(); i++) {
        commsEdit->append(QString::fromStdString(EMS[i]));
    }
}

void GUI::Author() {
    QMessageBox::about(NULL, "Author", "车明亮 \n10195102456");
}

void GUI::Introduce() {
    QDesktopServices::openUrl(QUrl("https://gitee.com/che-mingliang/small-c"));
}